package com.kedacom.ctsp.registry.service;

import com.kedacom.ctsp.codec.CodecFactory;
import com.kedacom.ctsp.codec.aes.AESCodec;
import com.kedacom.ctsp.javamail.ThymeleafMailSender;
import com.kedacom.ctsp.lang.RandomUtil;
import com.kedacom.ctsp.lang.StringUtil;
import com.kedacom.ctsp.lang.Validate;
import com.kedacom.ctsp.lang.exception.CommonException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.Base64;
import java.util.concurrent.TimeUnit;

/**
 * 默认UserAccount处理类
 *
 * @author sunchenjie
 */
@Slf4j
public abstract class AbstractUserAccountService implements UserAccountService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ThymeleafMailSender mailSender;

    /**
     * 项目url
     */
    @Value("${server.url}")
    private String serverUrl;

    /**
     * 过期时间 默认20mins
     */
    @Value("${expire-time.user.activate}")
    private Long activeExpireTime = 20L;
    @Value("${expire-time.user.retrieve-pwd}")
    private Long retrivePwdExpireTime = 20L;

    /**
     * redis前缀
     */
    @Value("${cstp.redis.prefix.user.activate}")
    private String userActivatePrefix = "USER_ACTIVE";
    @Value("${cstp.redis.prefix.user.retrieve-pwd}")
    private String userRetrivePwdPrefix = "USER_RETRIVE_PWD";


    private String key = "KedacomUrlKey";
    private AESCodec aesCodec = CodecFactory.aes(key);


    /**
     * 发送激活账号邮件
     *
     * @param userId
     * @return 1：表示成功发送 2：表示用户已激活，无需发送
     */
    @Override
    public int sendMailForActivateAccount(Long userId) {
        if (!checkUserUnActive(userId)) {
            log.info("[{}] 已激活，无需发送激活邮件", userId);
            return 2;
        }

        String userMail = getMailByUserId(userId);
        long time = System.currentTimeMillis() / 1000;

        String word = RandomUtil.randomString(6).toUpperCase().replace(",", "");

        // 加密url
        String urlPwd = encryptUrl(userId + "," + time + "," + word);
        // 以prefix+ userId作为主键
        redisTemplate.opsForValue().set(
                userActivatePrefix + ":" + userId, word, activeExpireTime, TimeUnit.MINUTES);

        String url = serverUrl.endsWith("/") ? serverUrl : serverUrl + "/";
        url = url + "account/activate?code=";

        // 真正的发送邮件
        // 需要实现
        sendMailDataForActivateAccount(userMail, url + urlPwd);

        return 1;
    }

    /**
     * 通过url激活账号
     */
    @Override
    public Long activateAccountByVerifyUrl(String verifyUrl) {
        String decode = null;
        try {
            decode = decryptUrl(verifyUrl);
        } catch (IllegalArgumentException e) {
            throw new CommonException("激活链接有误");
        }
        String[] split = decode.split(",");
        //能否解析
        Validate.isTrue(split.length == 3, "激活链接有误");

        Long userId = Long.valueOf(split[0]);
        Long createTime = Long.valueOf(split[1]);
        String word = split[2] == null ? "" : split[2].toUpperCase();
        Validate.isTrue(System.currentTimeMillis() / 1000 - createTime < activeExpireTime * 60
                , "激活链接已过期");

        String key = userActivatePrefix + ":" + userId;
        String value = redisTemplate.opsForValue().get(key);
        Validate.notBlank(value, "激活链接已失效，请重新激活");
        Validate.isTrue(value.equals(word), "激活链接有误");

        activeAccount(userId);
        redisTemplate.delete(key);
        return userId;
    }

    /**
     * 根据用户名 找回密码
     *
     * @param username
     */
    @Override
    public void retrievePasswordByUsername(String username) {
        Long userId = getUserIdByUsername(username);
        String userEmail = getMailByUserId(userId);
        String word = RandomUtil.randomString(6);

        // 以prefix+ userId作为主键
        redisTemplate.opsForValue().set(
                userRetrivePwdPrefix + ":" + userId, word, retrivePwdExpireTime, TimeUnit.MINUTES);

        // TODO 目前采用邮件发送验证码的方式
        mailSender.sendEmail(userEmail, "找回密码", "您的验证码为： " + word);
    }


    /**
     * 授权码验证
     *
     * @param username
     * @param code
     * @return 用户id
     */
    @Override
    public Long verifyByAuthenticationCode(String username, String code) {
        Long userId = getUserIdByUsername(username);

        // 以prefix+ userId作为主键
        String key = userRetrivePwdPrefix + ":" + userId;

        String word = redisTemplate.opsForValue().get(key);
        Validate.isTrue(!StringUtil.isNullOrEmpty(word), "验证码已失效，请重新发送");
        Validate.isTrue(word.equals(code), "验证码不正确");

        return userId;
    }

    /**
     * 授权码修改密码
     *
     * @param username
     * @param pwd
     * @param code
     * @return
     */
    @Override
    public boolean modifyPwdByAuthenticationCode(String username, String pwd, String code) {
        //验证code是否有效
        Long userId = verifyByAuthenticationCode(username, code);

        //真正的修改密码
        modifyUserPwd(userId, pwd);

        //删除redis数据，防止重复使用
        redisTemplate.delete(key);

        return true;
    }


    /**
     * aes加密，再base64编码
     *
     * @param code
     * @return
     */
    private String encryptUrl(String code) {
        byte[] encrypted = aesCodec.encrypt(code);
        byte[] base64encode = Base64.getEncoder().encode(encrypted);
        return new String(base64encode);
    }

    /**
     * base64解码 再解密
     *
     * @param code
     * @return
     */
    private String decryptUrl(String code) {
        byte[] base64decode = Base64.getDecoder().decode(code);
        byte[] decrypt = aesCodec.decrypt(base64decode);
        return new String(decrypt);
    }

}
